package com.csii.log.service.impl;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.StrUtil;
import com.csii.log.model.Audit;
import com.csii.log.properties.LogDbProperties;
import com.csii.log.service.IAuditService;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.scheduling.annotation.Async;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.time.ZoneId;
import java.util.Date;

/**
 * 审计日志实现类-数据库
 *
 * @author ian
 * @date 2020/10/23
 * <p>
 */
@Slf4j
@ConditionalOnProperty(name = "csii.audit-log.log-type", havingValue = "db")
@ConditionalOnClass(JdbcTemplate.class)
public class DbAuditServiceImpl implements IAuditService {
    private static final String INSERT_SQL = " insert into sys_log" +
            " (id,log_type,log_content,operate_type,userid,username,ip,method,request_url,request_param,request_type,cost_time,create_by,create_time,update_by,update_time,tenant_id,application_name,class_name) " +
            " values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";

    private final JdbcTemplate jdbcTemplate;

    private final Snowflake snowflake = new Snowflake(1,10);

    public DbAuditServiceImpl(@Autowired(required = false) LogDbProperties logDbProperties, DataSource dataSource) {
        //优先使用配置的日志数据源，否则使用默认的数据源
        if (logDbProperties != null && StrUtil.isNotEmpty(logDbProperties.getJdbcUrl())) {
            dataSource = new HikariDataSource(logDbProperties);
        }
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    @PostConstruct
    public void init() {
        String sql = "create table IF NOT EXISTS `sys_log` (" +
                "    id            varchar(32)   not null  primary key," +
                "    log_type      int(2)        null comment '日志类型（1登录日志，2操作日志）'," +
                "    log_content   varchar(1000) null comment '日志内容'," +
                "    operate_type  int(2)        null comment '操作类型'," +
                "    userid        varchar(32)   null comment '操作用户账号'," +
                "    username      varchar(100)  null comment '操作用户名称'," +
                "    ip            varchar(100)  null comment 'IP'," +
                "    method        varchar(500)  null comment '请求java方法'," +
                "    request_url   varchar(255)  null comment '请求路径'," +
                "    request_param longtext      null comment '请求参数'," +
                "    request_type  varchar(32)   null comment '请求类型'," +
                "    cost_time     bigint        null comment '耗时'," +
                "    create_by     varchar(32)   null comment '创建人'," +
                "    create_time   datetime      null comment '创建时间'," +
                "    update_by     varchar(32)   null comment '更新人'," +
                "    update_time   datetime      null comment '更新时间'," +
                "    tenant_id     varchar(32)   null comment '租户ID'," +
                "    application_name     varchar(36)   null comment '应用名称'," +
                "    class_name     varchar(32)   null comment '类名'" +
                ") comment '系统日志表' engine = INNODB charset = utf8;";
        try {
            String databaseType=this.jdbcTemplate.getDataSource().getConnection().getMetaData().getDatabaseProductName().toUpperCase();
             if(databaseType.indexOf("MYSQL")>=0)
                 this.jdbcTemplate.update(sql);
             else if(databaseType.indexOf("ORACLE")>=0){
                 SqlRowSet row= this.jdbcTemplate.queryForRowSet("select count(1)  from user_tables where table_name = UPPER('sys_log')");
                 if(row.next()&&row.getBigDecimal(1).intValue()==0)
                 {
                    sql="CREATE TABLE SYS_LOG (\n" +
                            "  ID VARCHAR2(32) NOT NULL ,\n" +
                            "  LOG_TYPE NUMBER(11) ,\n" +
                            "  LOG_CONTENT VARCHAR2(1000) ,\n" +
                            "  OPERATE_TYPE NUMBER(11) ,\n" +
                            "  USERID VARCHAR2(32) ,\n" +
                            "  USERNAME VARCHAR2(100) ,\n" +
                            "  IP VARCHAR2(100) ,\n" +
                            "  METHOD VARCHAR2(500) ,\n" +
                            "  REQUEST_URL VARCHAR2(255) ,\n" +
                            "  REQUEST_PARAM NCLOB ,\n" +
                            "  REQUEST_TYPE VARCHAR2(10) ,\n" +
                            "  COST_TIME NUMBER(20) ,\n" +
                            "  CREATE_BY VARCHAR2(32) ,\n" +
                            "  CREATE_TIME DATE ,\n" +
                            "  UPDATE_BY VARCHAR2(32) ,\n" +
                            "  UPDATE_TIME DATE ,\n" +
                            "  TENANT_ID VARCHAR2(36) ,\n" +
                            "  APPLICATION_NAME VARCHAR2(36) ,\n" +
                            "  CLASS_NAME VARCHAR2(150) \n" +
                            ")";
                     this.jdbcTemplate.update(sql);
                 }
             }

        } catch (SQLException e) {
            log.error(e.getMessage(),e);
        }
    }

    @Async
    @Override
    public void save(Audit audit) {
        this.jdbcTemplate.update(INSERT_SQL, snowflake.nextIdStr()
                ,audit.getLogType(), StringUtils.isEmpty(audit.getOperation())?"":audit.getOperation().length()>300?audit.getOperation().substring(0,300):audit.getOperation(),audit.getOperateType(),
                 audit.getUserId(), audit.getUserName(),audit.getIp(),audit.getMethodName(),audit.getUrl(),audit.getParams(),audit.getRequestType(),audit.getCostTime(),
                audit.getUserName(), Date.from(audit.getTimestamp().atZone(ZoneId.systemDefault()).toInstant()),audit.getUserName(),new Date(), audit.getTenantId(),audit.getApplicationName(),audit.getClassName());
    }
}
